In this notebook we will add TP53 alteration status as discussed in #837:

TP53 altered - loss, if :

Note: CNV and SV will be considered as the same event, but we will be adding SV_counts and SV.type to the altered status output file

TP53 altered - activated, if:

Setup

library("ggpubr")
Loading required package: ggplot2
Loading required package: magrittr
library("ggthemes")
library("tidyverse")
── Attaching packages ────────────────────────────────── tidyverse 1.2.1 ──
✔ tibble  2.1.3     ✔ purrr   0.3.2
✔ tidyr   0.8.3     ✔ dplyr   0.8.3
✔ readr   1.3.1     ✔ stringr 1.4.0
✔ tibble  2.1.3     ✔ forcats 0.4.0
── Conflicts ───────────────────────────────────── tidyverse_conflicts() ──
✖ tidyr::extract()   masks magrittr::extract()
✖ dplyr::filter()    masks stats::filter()
✖ dplyr::lag()       masks stats::lag()
✖ purrr::set_names() masks magrittr::set_names()
library("ggfortify")
library("broom")

# rootdir
root_dir <- rprojroot::find_root(rprojroot::has_dir(".git"))
data_dir <- file.path(root_dir, "data")

input_dir <- file.path(root_dir,
                         "analyses",
                         "tp53_nf1_score",
                         "input")
# cell composition
cell_line_composition <- read_tsv(file.path("..",
                                            "molecular-subtyping-HGG",
                                            "input",
                                            "cell-line-composition.tsv"),
                                  col_types = 
                                    readr::cols( aliquot_id  = readr::col_character())) 

# histology
if ( params$base_run ==0 ){
  clinical<-read.delim(file.path(data_dir,"histologies.tsv"), stringsAsFactors = FALSE)
} else{
  clinical<-read.delim(file.path(data_dir,"histologies-base.tsv"), stringsAsFactors = FALSE)  
}

# recording of "BS_ETC8R0TD" GMKF NBL sample; needs to be remove once updated in v12
clinical$RNA_library[clinical$Kids_First_Biospecimen_ID == "BS_ETC8R0TD"] <- "stranded"

histology <- clinical %>%
  dplyr::select("Kids_First_Biospecimen_ID",
        "sample_id",
        "Kids_First_Participant_ID",
        "cancer_predispositions",
        "sample_type",
        "experimental_strategy",
        "composition",
        "cohort") %>%
   # merge cell line composition
  left_join(cell_line_composition)
Joining, by = c("Kids_First_Biospecimen_ID", "sample_id", "Kids_First_Participant_ID")
# Cancer database
hotspot_database_2017_tp53_snv <- readxl::read_xls(file.path(input_dir,"hotspots_v2.xls"),sheet = 1) %>%
  filter(Hugo_Symbol == "TP53")
hotspot_database_2017_tp53_indel <- readxl::read_xls(file.path(input_dir,"hotspots_v2.xls"),sheet = 2) %>%
  filter(Hugo_Symbol == "TP53")

# p53, p63 and p73 functional sites
functional_sites_tp53 <- read_tsv(file.path(input_dir,"hotspot_Chen_2006.tsv")) 
Parsed with column specification:
cols(
  p53 = col_character(),
  p63 = col_character(),
  p73 = col_character()
)
results_dir <- file.path(root_dir,
                         "analyses",
                         "tp53_nf1_score",
                         "results")

if (!dir.exists(results_dir)) {
  dir.create(results_dir)
}

# Read in combined scores from tp53-nf1-classifier 
classifier_score <- read_tsv(file.path(results_dir, "combined_scores.tsv")) %>%
  dplyr::rename(Kids_First_Biospecimen_ID_RNA = sample_id) %>%
  filter(Kids_First_Biospecimen_ID_RNA %in% histology$Kids_First_Biospecimen_ID) %>%
  left_join(histology, by=c("Kids_First_Biospecimen_ID_RNA"="Kids_First_Biospecimen_ID"))
Parsed with column specification:
cols(
  sample_id = col_character(),
  tp53_score = col_double(),
  tp53_shuffle = col_double()
)
# Copy number per sample 
# overlapping functional domain
cnv_domain_overlap <- read_tsv(
  file.path(
    results_dir,
    "loss_overlap_domains_tp53.tsv"))
Parsed with column specification:
cols(
  biospecimen_id = col_character(),
  copy_number = col_double(),
  ploidy = col_double(),
  domain = col_character(),
  Kids_First_Participant_ID = col_character(),
  sample_id = col_character(),
  composition = col_character(),
  tumor_descriptor = col_character(),
  tp53_shuffle = col_double()
)
# Structural variant overlapping 
# or within gene locus of TP53
sv_overlap <- read_tsv(
  file.path(
    results_dir,
    "sv_overlap_tp53.tsv"))
Parsed with column specification:
cols(
  SV.chrom = col_double(),
  SV.start = col_double(),
  SV.end = col_double(),
  SV.length = col_double(),
  Kids.First.Biospecimen.ID.Tumor = col_character(),
  SV.type = col_character(),
  Gene.name = col_character(),
  ALT = col_character(),
  sample_id = col_character()
)
# Structural variant as a fusion in exon1/intron1
fusion_overlap <- read_tsv(
  file.path(
    results_dir,
    "fusion_bk_tp53_loss.tsv"))
Parsed with column specification:
cols(
  seqnames = col_character(),
  start = col_double(),
  end = col_double(),
  width = col_double(),
  strand = col_character(),
  Sample = col_character(),
  sample_id = col_character(),
  FusionName = col_character(),
  variable = col_character(),
  value = col_time(format = "")
)
Warning: 73 parsing failures.
row   col   expected     actual                                                                                        file
  1 value valid date 17:7687377 '/home/rstudio/OpenPedCan-analysis/analyses/tp53_nf1_score/results/fusion_bk_tp53_loss.tsv'
  2 value valid date 17:7687377 '/home/rstudio/OpenPedCan-analysis/analyses/tp53_nf1_score/results/fusion_bk_tp53_loss.tsv'
  3 value valid date 17:7687377 '/home/rstudio/OpenPedCan-analysis/analyses/tp53_nf1_score/results/fusion_bk_tp53_loss.tsv'
  4 value valid date 17:7687377 '/home/rstudio/OpenPedCan-analysis/analyses/tp53_nf1_score/results/fusion_bk_tp53_loss.tsv'
  5 value valid date 17:7687377 '/home/rstudio/OpenPedCan-analysis/analyses/tp53_nf1_score/results/fusion_bk_tp53_loss.tsv'
... ..... .......... .......... ...........................................................................................
See problems(...) for more details.

Check if all function sites are in hotspots

All functional sites are just 1 base so checking in hotspot_database_2017_snv

functional_sites_tp53 %>%
  filter(!gsub("[A-Z|a-z]","",p53) %in% hotspot_database_2017_tp53_snv$Amino_Acid_Position )

2 functional sites are missing in hotspots database.

SNV for TP53

Removing Silent or Intron classified variants to capture only putative damaging mutations

consensus_tp53_snv_indel <- data.table::fread(
  file.path(data_dir,"snv-consensus-plus-hotspots.maf.tsv.gz"),
                                   select = c("Chromosome",
                                              "Start_Position",
                                              "End_Position",
                                              "Strand",
                                              "Variant_Classification",
                                              "Tumor_Sample_Barcode",
                                              "Hugo_Symbol",
                                              "HGVSp_Short"),
                                   data.table = FALSE) %>%
  filter(Hugo_Symbol == "TP53") %>%
  filter(!(Variant_Classification %in% c("Silent", "Intron",
                                         # remove other non-amino acid SNVs
                                         "3'Flank" ,"5'Flank",
                                         "3'UTR", "5'UTR"  )))
Registered S3 method overwritten by 'R.oo':
  method        from       
  throw.default R.methodsS3

Gather annotation for SNV

hotspots : overlaps AA position which are statistically significant SNV/Indels activating : overlaps AA position which are found to act as gain-of-function according to literature

consensus_tp53_snv_indel <- consensus_tp53_snv_indel %>%
  mutate(
    hotspot = case_when(
      # strip REF and Variant AA to get Amino_Acid_Position in consensus maf
      (gsub("[A-Z|a-z]|[.]","",HGVSp_Short) %in% 
         # if overlaps the hotspot Amino_Acid_Position 
         hotspot_database_2017_tp53_snv$Amino_Acid_Position)  ~ 1,
      TRUE ~ 0),
    activating = case_when(
      # strip REF and Variant AA to get Amino_Acid_Position in consensus maf
      (gsub("[A-Z|a-z]|[.]","",HGVSp_Short) %in% 
         # if overlaps the activating  Amino_Acid_Position 
         c("273","248"))  ~ 1,
      TRUE ~ 0
    )
  )

Gather SNV,CNV, classifier and cancer_predisposition values

We will gather values per sample_id since DNA and RNA samples can be only matched by sample_id

tp53_alterations <- histology %>%
  filter(experimental_strategy != "RNA-Seq") %>%
  # filter for Tumors
  filter(sample_type=="Tumor") %>%
  # join consensus calls overlapping hotspots
  left_join(consensus_tp53_snv_indel, 
            by=c("Kids_First_Biospecimen_ID"="Tumor_Sample_Barcode")) %>%
  # join filtered cnv losses
  left_join(cnv_domain_overlap,by=c("Kids_First_Biospecimen_ID"="biospecimen_id",
                                    "sample_id", "Kids_First_Participant_ID", 
                                    "composition")) %>%
  # join SV 
  left_join(sv_overlap,by=c("Kids_First_Biospecimen_ID"="Kids.First.Biospecimen.ID.Tumor",
                            "sample_id")) %>%
  dplyr::rename(Kids_First_Biospecimen_ID_DNA = Kids_First_Biospecimen_ID) %>%
  # join fusion
  left_join(fusion_overlap,by="sample_id") 

tp53_alterations_score <- tp53_alterations %>%
  # add classifier score
  full_join(classifier_score,by=c("sample_id",
                                  "composition",
                                  "cell_line_composition",
                                  "cancer_predispositions",
                                  "sample_type",
                                  "Kids_First_Participant_ID",
                                  "cohort")) %>%
  # select useful columns
  dplyr::select(sample_id,Kids_First_Biospecimen_ID_RNA,Kids_First_Biospecimen_ID_DNA,
         tp53_score,cancer_predispositions,HGVSp_Short,copy_number,SV.type,FusionName,hotspot,activating) %>%
  arrange(sample_id) %>%
  unique() %>%
  replace_na(list(hotspot = 0, activating = 0))

tp53_alterations_score 

Convert the above to wide format

tp53_alterations_score_wide <- tp53_alterations_score %>%
  # group 
  group_by(sample_id,
           Kids_First_Biospecimen_ID_DNA,
           Kids_First_Biospecimen_ID_RNA,
           cancer_predispositions,
           tp53_score) %>%
  # 
  # sample_id as rows add SNV counts,CNV loss counts 
  # and hotspot/activating mutation annotation  
  summarise(
    # summarize length of SNV and CNV alterations 
   SNV_indel_counts= length(unique(HGVSp_Short[!is.na(HGVSp_Short)])), 
    CNV_loss_counts = length(unique(copy_number[!is.na(copy_number)])),
    SV_counts = length(unique(SV.type[!is.na(SV.type)])),
    Fusion_counts = length(unique(FusionName[!is.na(FusionName)])),
    HGVSp_Short = toString(unique(HGVSp_Short)),
    CNV_loss_evidence = toString(unique(copy_number)),
    SV_type = toString(unique(SV.type)),
    Fusion_evidence = toString(unique(FusionName)),
    # summarize unique hotspot values per sample_id
    hotspot = max(unique(hotspot[!is.na(hotspot) ])),
    activating = max(unique(activating[!is.na(activating)]))) 

tp53_alterations_score_wide

Add annotation

As discussed above we want to annotate TP53 mutants that are putative loss-of-function OR gain-of-function.

tp53_alterations_score_wide <- tp53_alterations_score_wide %>%
  mutate(
    # add tp53_altered annotation column
    tp53_altered = 
      case_when(
        # when activating == 0
        activating == 0 &
          # check if mutated variant AA position overlaps hotspot database 
          ( hotspot == 1  |
              # check if sample_id has SNV+(CNV|SV) mutation
              # suggesting both alleles are mutated
              (SNV_indel_counts >= 1 & 
                 (CNV_loss_counts >=1 | SV_counts >=1) ) |
              # check if more than 1 SNV is present
              # suggesting both alleles are mutated
              SNV_indel_counts > 1 |
              # check if SNV mutant and has
              # Li-Fraumeni syndrome suggesting
              # germline TP53 mutant as cancer predisposition 
              (SNV_indel_counts >= 1 & 
                             grepl("Li-Fraumeni syndrome",cancer_predispositions)) |
              # check if CNV|SV mutant  and has
              # Li-Fraumeni syndrome suggesting
              # germline TP53 mutant as cancer predisposition 
              ((CNV_loss_counts >= 1 | SV_counts >=1 ) & 
                             grepl("Li-Fraumeni syndrome",cancer_predispositions)) |
              # check if tp53 inactivating score for RNA_Seq is greater that 0.5
              # and has Li-Fraumeni syndrome suggesting
              # germline TP53 mutant as cancer predisposition 
              (tp53_score > 0.5 & 
                             grepl("Li-Fraumeni syndrome",cancer_predispositions)) |
              # check if tp53 inactivating score for RNA_Seq is greater that 0.5
              # and has 1 or more SNV| (CNV|SV) loss in TP53
              (tp53_score > 0.5 & 
                             (SNV_indel_counts >= 1 | (CNV_loss_counts >=1 | SV_counts >=1 )))
          ) ~ "loss",
        # when activating == 1
        activating == 1 ~ "activated",
        # if no evidence supports TP53 inativation or activation 
        TRUE ~ "Other"
      )
  )

Explore distribution of tp53_altered status vs tp53 inactivation scores

ggplot(tp53_alterations_score_wide, aes(x = factor(tp53_altered), y = tp53_score)) +
  geom_violin()+
  geom_jitter(alpha = 0.5, width = 0.2) +
  stat_compare_means() +
  theme_bw() +
  ggtitle("Distribution of scores across tp53 altered status") +
  theme(axis.text.x = element_text(angle = 60, hjust = 1))+
  xlab("tp53 altered status") 
Warning: Removed 3546 rows containing non-finite values (stat_ydensity).
Warning: Removed 3546 rows containing non-finite values
(stat_compare_means).
Warning: Removed 3546 rows containing missing values (geom_point).

TP53 mutants with gain-of-function (activating) mutants promote tumorigenesis according to literature. Reference and reference have similar distribution of classifier scores to that of potential loss-of-function (multi-allelic) Tp53 mutations.

“Other” annotated samples either don’t have any high-confidence loss/gain TP53 SNVs nor CNV losses OR DNA sample is not available.

Expression profile for activating vs loss TP53 status

expr_combined <- readRDS(file.path(data_dir,"gene-counts-rsem-expected_count-collapsed.rds"))

Expression profile for activating vs loss TP53 status

# iterate through all possible RNA library type 
# first filter to 
clinical_filtered <- clinical %>% 
  filter(Kids_First_Biospecimen_ID %in% names(expr_combined))

# generate list of all RNA library type
rna_library_list <- clinical_filtered %>% 
  filter(Kids_First_Biospecimen_ID %in% names(expr_combined)) %>%
  pull(RNA_library) %>% 
  unique()

for(i in 1:length(rna_library_list)) {
  
  library_each <- rna_library_list[[i]]
  # pull BS IDs for that RNA library type
  library_samples <- clinical_filtered %>% 
    filter(RNA_library == library_each) %>%
    pull(Kids_First_Biospecimen_ID) %>% 
    unique()
  # subset expression matrix using that
  expr_each <- expr_combined %>% 
    dplyr::select(library_samples)

  # subset to TP53
  subset_expr_each <- t(expr_each)[,"TP53"] %>% 
    as.data.frame() 
  colnames(subset_expr_each) <- "TP53"
  
  # combine the TP53 alteration information
  subset_expr_combined <- subset_expr_each %>%
    tibble::rownames_to_column() %>% 
    left_join(tp53_alterations_score_wide,by=c("rowname"="Kids_First_Biospecimen_ID_RNA")) %>%
    filter(tp53_altered %in% c("activated","loss"))
  
  # plot distribution of TP53 gene expression 
  plot<- ggplot(subset_expr_combined, aes(x = factor(tp53_altered), y = TP53)) +
    geom_violin()+
    geom_jitter(alpha = 0.5, width = 0.2) +
    stat_compare_means() +
    theme_bw() +
    ggtitle("Distribution of TP53 expression across tp53 altered status") +
    theme(axis.text.x = element_text(angle = 60, hjust = 1))+
    xlab(paste0("tp53 altered status wth RNA library ", library_each))

  print(plot)
}

Check if other cancer predisposition have high TP53 inactivation scores

ggplot(tp53_alterations_score_wide, aes(x = factor(tp53_altered), y = tp53_score)) +
  geom_violin()+
  geom_jitter(alpha = 0.5, width = 0.2) +
  theme_bw() +
  ggtitle("Distribution of scores across tp53 altered status") +
  theme(axis.text.x = element_text(angle = 60, hjust = 1))+
  xlab("tp53 altered status") +
  facet_wrap(.~cancer_predispositions)
Warning: Removed 3546 rows containing non-finite values (stat_ydensity).
Warning: Removed 3546 rows containing missing values (geom_point).

Some NF-1 and/or Other inherited conditions NOS have high scoring TP53 mutants as well.

Interesting 2 bs ids annotated with Li-Fraumeni synfrome pre-disposition have very low tp53 classifier scores.

tp53_alterations_score_wide %>%
  filter(cancer_predispositions == "Li-Fraumeni syndrome",
         tp53_score < 0.5) 

Save file

Adding DNA and RNA biospecimen ids to tp53_alterations_score_wide and saving

tp53_alterations_score_wide %>% 
  write_tsv(file.path(results_dir,"tp53_altered_status.tsv"))
LS0tCnRpdGxlOiAiVHA1MyBTTlYgaG90c3BvdHMiCmF1dGhvcjogIksgUyBHYW9ua2FyIChEM0IpIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKcGFyYW1zOgogIGJhc2VfcnVuOgogICAgbGFiZWw6ICIxLzAgdG8gcnVuIHdpdGggYmFzZSBoaXN0b2xvZ3kiCiAgICB2YWx1ZTogMAogICAgaW5wdXQ6IGludGVnZXIKLS0tCgpJbiB0aGlzIG5vdGVib29rIHdlIHdpbGwgYWRkIFRQNTMgYWx0ZXJhdGlvbiBzdGF0dXMgYXMgZGlzY3Vzc2VkIGluIFsjODM3XShodHRwczovL2dpdGh1Yi5jb20vQWxleHNMZW1vbmFkZS9PcGVuUEJUQS1hbmFseXNpcy9pc3N1ZXMvODM3KToKCioqVFA1MyBhbHRlcmVkIC0gbG9zcyoqLCBpZiA6CgogLSBBIHNhbXBsZSBjb250YWlucyBhIFRQNTMgaG90c3BvdCBTTlYgbXV0YXRpb24uIChDYW5jZXIgaG90c3BvdCBkYXRhYmFzZSBhbmQgZG93bmxvYWRhYmxlIGZpbGUgYXZhaWxhYmxlKS4gUGxlYXNlIGFsc28gY3Jvc3NjaGVjayB0aGF0IGFsbCBtdXRhdGlvbnMgZnJvbSB0aGlzIHRhYmxlIGFyZSBpbmNsdWRlZC4KIC0gQSBzYW1wbGUgY29udGFpbnMgdHdvIFRQNTMgYWx0ZXJhdGlvbnMsIHN1Z2dlc3RpbmcgKGJ1dCBub3QgY29uZmlybWluZykgdGhhdCBib3RoIGFsbGVsZXMgYXJlIGFmZmVjdGVkIChTTlYrU05WLCAoQ05WIG9yIFNWKSArIFNOVikuCiAtIEEgc2FtcGxlIGNvbnRhaW5zIG9uZSBhbHRlcmF0aW9uIChTTlYgb3IgKENOViBvciBTVikpICsgaGFzIGNhbmNlcl9wcmVkaXNwb3NpdGlvbnMgPT0gIkxpLUZyYXVtZW5pIHN5bmRyb21lIiwgc3VnZ2VzdGluZyB0aGVyZSBpcyBhIGdlcm1saW5lIHZhcmlhbnQgaW4gYWRkaXRpb24gdG8gdGhlIHNvbWF0aWMgdmFyaWFudCB3ZSBvYnNlcnZlLgogLSBBIHNhbXBsZSBkb2VzIG5vdCBoYXZlIGEgVFA1MyBhbHRlcmF0aW9ucywgYnV0IGhhcyBjYW5jZXJfcHJlZGlzcG9zaXRpb25zID09ICJMaS1GcmF1bWVuaSBzeW5kcm9tZSIgYW5kIFRQNTMgY2xhc3NpZmllciBzY29yZSBmb3IgbWF0Y2hlZCBSTkEtU2VxID4gMC41IChvciBoaWdoZXIgY3V0b2ZmIHdlIGRlY2lkZSB1cG9uIGxhdGVyKS4KIAogTm90ZTogQ05WIGFuZCBTViB3aWxsIGJlIGNvbnNpZGVyZWQgYXMgdGhlIHNhbWUgZXZlbnQsIGJ1dCB3ZSB3aWxsIGJlIGFkZGluZyBTVl9jb3VudHMgYW5kIFNWLnR5cGUgdG8gdGhlIGFsdGVyZWQgc3RhdHVzIG91dHB1dCBmaWxlCiAKKipUUDUzIGFsdGVyZWQgLSBhY3RpdmF0ZWQqKiwgaWY6CgotIEEgc2FtcGxlIGNvbnRhaW5zIG9uZSBvZiB0aGUgdHdvIFRQNTMgYWN0aXZhdGluZyBtdXRhdGlvbnMgcC5SMjczQyBhbmQgcC5SMjQ4Vy4gW0Bkb2k6MTAuMTAzOC9uZzA1OTMtNDJdCgojIyBTZXR1cApgYGB7cn0KbGlicmFyeSgiZ2dwdWJyIikKbGlicmFyeSgiZ2d0aGVtZXMiKQpsaWJyYXJ5KCJ0aWR5dmVyc2UiKQpsaWJyYXJ5KCJnZ2ZvcnRpZnkiKQpsaWJyYXJ5KCJicm9vbSIpCgojIHJvb3RkaXIKcm9vdF9kaXIgPC0gcnByb2pyb290OjpmaW5kX3Jvb3QocnByb2pyb290OjpoYXNfZGlyKCIuZ2l0IikpCmRhdGFfZGlyIDwtIGZpbGUucGF0aChyb290X2RpciwgImRhdGEiKQoKaW5wdXRfZGlyIDwtIGZpbGUucGF0aChyb290X2RpciwKICAgICAgICAgICAgICAgICAgICAgICAgICJhbmFseXNlcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAidHA1M19uZjFfc2NvcmUiLAogICAgICAgICAgICAgICAgICAgICAgICAgImlucHV0IikKIyBjZWxsIGNvbXBvc2l0aW9uCmNlbGxfbGluZV9jb21wb3NpdGlvbiA8LSByZWFkX3RzdihmaWxlLnBhdGgoIi4uIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibW9sZWN1bGFyLXN1YnR5cGluZy1IR0ciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJpbnB1dCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImNlbGwtbGluZS1jb21wb3NpdGlvbi50c3YiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbF90eXBlcyA9IAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWFkcjo6Y29scyggYWxpcXVvdF9pZCAgPSByZWFkcjo6Y29sX2NoYXJhY3RlcigpKSkgCgojIGhpc3RvbG9neQppZiAoIHBhcmFtcyRiYXNlX3J1biA9PTAgKXsKICBjbGluaWNhbDwtcmVhZC5kZWxpbShmaWxlLnBhdGgoZGF0YV9kaXIsImhpc3RvbG9naWVzLnRzdiIpLCBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpCn0gZWxzZXsKICBjbGluaWNhbDwtcmVhZC5kZWxpbShmaWxlLnBhdGgoZGF0YV9kaXIsImhpc3RvbG9naWVzLWJhc2UudHN2IiksIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkgIAp9CgojIHJlY29yZGluZyBvZiAiQlNfRVRDOFIwVEQiIEdNS0YgTkJMIHNhbXBsZTsgbmVlZHMgdG8gYmUgcmVtb3ZlIG9uY2UgdXBkYXRlZCBpbiB2MTIKY2xpbmljYWwkUk5BX2xpYnJhcnlbY2xpbmljYWwkS2lkc19GaXJzdF9CaW9zcGVjaW1lbl9JRCA9PSAiQlNfRVRDOFIwVEQiXSA8LSAic3RyYW5kZWQiCgpoaXN0b2xvZ3kgPC0gY2xpbmljYWwgJT4lCiAgZHBseXI6OnNlbGVjdCgiS2lkc19GaXJzdF9CaW9zcGVjaW1lbl9JRCIsCiAgICAgICAgInNhbXBsZV9pZCIsCiAgICAgICAgIktpZHNfRmlyc3RfUGFydGljaXBhbnRfSUQiLAogICAgICAgICJjYW5jZXJfcHJlZGlzcG9zaXRpb25zIiwKICAgICAgICAic2FtcGxlX3R5cGUiLAogICAgICAgICJleHBlcmltZW50YWxfc3RyYXRlZ3kiLAogICAgICAgICJjb21wb3NpdGlvbiIsCiAgICAgICAgImNvaG9ydCIpICU+JQogICAjIG1lcmdlIGNlbGwgbGluZSBjb21wb3NpdGlvbgogIGxlZnRfam9pbihjZWxsX2xpbmVfY29tcG9zaXRpb24pCiAKCiMgQ2FuY2VyIGRhdGFiYXNlCmhvdHNwb3RfZGF0YWJhc2VfMjAxN190cDUzX3NudiA8LSByZWFkeGw6OnJlYWRfeGxzKGZpbGUucGF0aChpbnB1dF9kaXIsImhvdHNwb3RzX3YyLnhscyIpLHNoZWV0ID0gMSkgJT4lCiAgZmlsdGVyKEh1Z29fU3ltYm9sID09ICJUUDUzIikKaG90c3BvdF9kYXRhYmFzZV8yMDE3X3RwNTNfaW5kZWwgPC0gcmVhZHhsOjpyZWFkX3hscyhmaWxlLnBhdGgoaW5wdXRfZGlyLCJob3RzcG90c192Mi54bHMiKSxzaGVldCA9IDIpICU+JQogIGZpbHRlcihIdWdvX1N5bWJvbCA9PSAiVFA1MyIpCgojIHA1MywgcDYzIGFuZCBwNzMgZnVuY3Rpb25hbCBzaXRlcwpmdW5jdGlvbmFsX3NpdGVzX3RwNTMgPC0gcmVhZF90c3YoZmlsZS5wYXRoKGlucHV0X2RpciwiaG90c3BvdF9DaGVuXzIwMDYudHN2IikpIAoKcmVzdWx0c19kaXIgPC0gZmlsZS5wYXRoKHJvb3RfZGlyLAogICAgICAgICAgICAgICAgICAgICAgICAgImFuYWx5c2VzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICJ0cDUzX25mMV9zY29yZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAicmVzdWx0cyIpCgppZiAoIWRpci5leGlzdHMocmVzdWx0c19kaXIpKSB7CiAgZGlyLmNyZWF0ZShyZXN1bHRzX2RpcikKfQoKIyBSZWFkIGluIGNvbWJpbmVkIHNjb3JlcyBmcm9tIHRwNTMtbmYxLWNsYXNzaWZpZXIgCmNsYXNzaWZpZXJfc2NvcmUgPC0gcmVhZF90c3YoZmlsZS5wYXRoKHJlc3VsdHNfZGlyLCAiY29tYmluZWRfc2NvcmVzLnRzdiIpKSAlPiUKICBkcGx5cjo6cmVuYW1lKEtpZHNfRmlyc3RfQmlvc3BlY2ltZW5fSURfUk5BID0gc2FtcGxlX2lkKSAlPiUKICBmaWx0ZXIoS2lkc19GaXJzdF9CaW9zcGVjaW1lbl9JRF9STkEgJWluJSBoaXN0b2xvZ3kkS2lkc19GaXJzdF9CaW9zcGVjaW1lbl9JRCkgJT4lCiAgbGVmdF9qb2luKGhpc3RvbG9neSwgYnk9YygiS2lkc19GaXJzdF9CaW9zcGVjaW1lbl9JRF9STkEiPSJLaWRzX0ZpcnN0X0Jpb3NwZWNpbWVuX0lEIikpCgojIENvcHkgbnVtYmVyIHBlciBzYW1wbGUgCiMgb3ZlcmxhcHBpbmcgZnVuY3Rpb25hbCBkb21haW4KY252X2RvbWFpbl9vdmVybGFwIDwtIHJlYWRfdHN2KAogIGZpbGUucGF0aCgKICAgIHJlc3VsdHNfZGlyLAogICAgImxvc3Nfb3ZlcmxhcF9kb21haW5zX3RwNTMudHN2IikpCgojIFN0cnVjdHVyYWwgdmFyaWFudCBvdmVybGFwcGluZyAKIyBvciB3aXRoaW4gZ2VuZSBsb2N1cyBvZiBUUDUzCnN2X292ZXJsYXAgPC0gcmVhZF90c3YoCiAgZmlsZS5wYXRoKAogICAgcmVzdWx0c19kaXIsCiAgICAic3Zfb3ZlcmxhcF90cDUzLnRzdiIpKQoKIyBTdHJ1Y3R1cmFsIHZhcmlhbnQgYXMgYSBmdXNpb24gaW4gZXhvbjEvaW50cm9uMQpmdXNpb25fb3ZlcmxhcCA8LSByZWFkX3RzdigKICBmaWxlLnBhdGgoCiAgICByZXN1bHRzX2RpciwKICAgICJmdXNpb25fYmtfdHA1M19sb3NzLnRzdiIpKQoKYGBgCgojIyMgQ2hlY2sgaWYgYWxsIGZ1bmN0aW9uIHNpdGVzIGFyZSBpbiBob3RzcG90cwoKQWxsIGZ1bmN0aW9uYWwgc2l0ZXMgYXJlIGp1c3QgMSBiYXNlIHNvIGNoZWNraW5nIGluIGBob3RzcG90X2RhdGFiYXNlXzIwMTdfc252YAoKYGBge3J9CgpmdW5jdGlvbmFsX3NpdGVzX3RwNTMgJT4lCiAgZmlsdGVyKCFnc3ViKCJbQS1afGEtel0iLCIiLHA1MykgJWluJSBob3RzcG90X2RhdGFiYXNlXzIwMTdfdHA1M19zbnYkQW1pbm9fQWNpZF9Qb3NpdGlvbiApCgpgYGAKMiBmdW5jdGlvbmFsIHNpdGVzIGFyZSBtaXNzaW5nIGluIGhvdHNwb3RzIGRhdGFiYXNlLgoKIyMgU05WIGZvciBUUDUzCgpSZW1vdmluZyBTaWxlbnQgb3IgSW50cm9uIGNsYXNzaWZpZWQgdmFyaWFudHMgdG8gY2FwdHVyZSBvbmx5IHB1dGF0aXZlIGRhbWFnaW5nIG11dGF0aW9ucwoKYGBge3J9CmNvbnNlbnN1c190cDUzX3Nudl9pbmRlbCA8LSBkYXRhLnRhYmxlOjpmcmVhZCgKICBmaWxlLnBhdGgoZGF0YV9kaXIsInNudi1jb25zZW5zdXMtcGx1cy1ob3RzcG90cy5tYWYudHN2Lmd6IiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VsZWN0ID0gYygiQ2hyb21vc29tZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiU3RhcnRfUG9zaXRpb24iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkVuZF9Qb3NpdGlvbiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiU3RyYW5kIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJWYXJpYW50X0NsYXNzaWZpY2F0aW9uIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJUdW1vcl9TYW1wbGVfQmFyY29kZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiSHVnb19TeW1ib2wiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkhHVlNwX1Nob3J0IiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0YS50YWJsZSA9IEZBTFNFKSAlPiUKICBmaWx0ZXIoSHVnb19TeW1ib2wgPT0gIlRQNTMiKSAlPiUKICBmaWx0ZXIoIShWYXJpYW50X0NsYXNzaWZpY2F0aW9uICVpbiUgYygiU2lsZW50IiwgIkludHJvbiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyByZW1vdmUgb3RoZXIgbm9uLWFtaW5vIGFjaWQgU05WcwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIzJ0ZsYW5rIiAsIjUnRmxhbmsiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIzJ1VUUiIsICI1J1VUUiIgICkpKQpgYGAKCiMjIyAgR2F0aGVyIGFubm90YXRpb24gZm9yIFNOViAKCmhvdHNwb3RzIDogb3ZlcmxhcHMgQUEgcG9zaXRpb24gd2hpY2ggYXJlIHN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQgU05WL0luZGVscwphY3RpdmF0aW5nIDogb3ZlcmxhcHMgQUEgcG9zaXRpb24gd2hpY2ggYXJlIGZvdW5kIHRvIGFjdCBhcyBnYWluLW9mLWZ1bmN0aW9uIGFjY29yZGluZyB0byBsaXRlcmF0dXJlCgpgYGB7cn0KCmNvbnNlbnN1c190cDUzX3Nudl9pbmRlbCA8LSBjb25zZW5zdXNfdHA1M19zbnZfaW5kZWwgJT4lCiAgbXV0YXRlKAogICAgaG90c3BvdCA9IGNhc2Vfd2hlbigKICAgICAgIyBzdHJpcCBSRUYgYW5kIFZhcmlhbnQgQUEgdG8gZ2V0IEFtaW5vX0FjaWRfUG9zaXRpb24gaW4gY29uc2Vuc3VzIG1hZgogICAgICAoZ3N1YigiW0EtWnxhLXpdfFsuXSIsIiIsSEdWU3BfU2hvcnQpICVpbiUgCiAgICAgICAgICMgaWYgb3ZlcmxhcHMgdGhlIGhvdHNwb3QgQW1pbm9fQWNpZF9Qb3NpdGlvbiAKICAgICAgICAgaG90c3BvdF9kYXRhYmFzZV8yMDE3X3RwNTNfc252JEFtaW5vX0FjaWRfUG9zaXRpb24pICB+IDEsCiAgICAgIFRSVUUgfiAwKSwKICAgIGFjdGl2YXRpbmcgPSBjYXNlX3doZW4oCiAgICAgICMgc3RyaXAgUkVGIGFuZCBWYXJpYW50IEFBIHRvIGdldCBBbWlub19BY2lkX1Bvc2l0aW9uIGluIGNvbnNlbnN1cyBtYWYKICAgICAgKGdzdWIoIltBLVp8YS16XXxbLl0iLCIiLEhHVlNwX1Nob3J0KSAlaW4lIAogICAgICAgICAjIGlmIG92ZXJsYXBzIHRoZSBhY3RpdmF0aW5nICBBbWlub19BY2lkX1Bvc2l0aW9uIAogICAgICAgICBjKCIyNzMiLCIyNDgiKSkgIH4gMSwKICAgICAgVFJVRSB+IDAKICAgICkKICApCgpgYGAKCiMjIEdhdGhlciBTTlYsQ05WLCBjbGFzc2lmaWVyIGFuZCBjYW5jZXJfcHJlZGlzcG9zaXRpb24gdmFsdWVzCgpXZSB3aWxsIGdhdGhlciB2YWx1ZXMgcGVyIHNhbXBsZV9pZCBzaW5jZSBETkEgYW5kIFJOQSBzYW1wbGVzIGNhbiBiZSBvbmx5IG1hdGNoZWQgYnkgYHNhbXBsZV9pZGAKCgpgYGB7cn0KCnRwNTNfYWx0ZXJhdGlvbnMgPC0gaGlzdG9sb2d5ICU+JQogIGZpbHRlcihleHBlcmltZW50YWxfc3RyYXRlZ3kgIT0gIlJOQS1TZXEiKSAlPiUKICAjIGZpbHRlciBmb3IgVHVtb3JzCiAgZmlsdGVyKHNhbXBsZV90eXBlPT0iVHVtb3IiKSAlPiUKICAjIGpvaW4gY29uc2Vuc3VzIGNhbGxzIG92ZXJsYXBwaW5nIGhvdHNwb3RzCiAgbGVmdF9qb2luKGNvbnNlbnN1c190cDUzX3Nudl9pbmRlbCwgCiAgICAgICAgICAgIGJ5PWMoIktpZHNfRmlyc3RfQmlvc3BlY2ltZW5fSUQiPSJUdW1vcl9TYW1wbGVfQmFyY29kZSIpKSAlPiUKICAjIGpvaW4gZmlsdGVyZWQgY252IGxvc3NlcwogIGxlZnRfam9pbihjbnZfZG9tYWluX292ZXJsYXAsYnk9YygiS2lkc19GaXJzdF9CaW9zcGVjaW1lbl9JRCI9ImJpb3NwZWNpbWVuX2lkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNhbXBsZV9pZCIsICJLaWRzX0ZpcnN0X1BhcnRpY2lwYW50X0lEIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJjb21wb3NpdGlvbiIpKSAlPiUKICAjIGpvaW4gU1YgCiAgbGVmdF9qb2luKHN2X292ZXJsYXAsYnk9YygiS2lkc19GaXJzdF9CaW9zcGVjaW1lbl9JRCI9IktpZHMuRmlyc3QuQmlvc3BlY2ltZW4uSUQuVHVtb3IiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgInNhbXBsZV9pZCIpKSAlPiUKICBkcGx5cjo6cmVuYW1lKEtpZHNfRmlyc3RfQmlvc3BlY2ltZW5fSURfRE5BID0gS2lkc19GaXJzdF9CaW9zcGVjaW1lbl9JRCkgJT4lCiAgIyBqb2luIGZ1c2lvbgogIGxlZnRfam9pbihmdXNpb25fb3ZlcmxhcCxieT0ic2FtcGxlX2lkIikgCgp0cDUzX2FsdGVyYXRpb25zX3Njb3JlIDwtIHRwNTNfYWx0ZXJhdGlvbnMgJT4lCiAgIyBhZGQgY2xhc3NpZmllciBzY29yZQogIGZ1bGxfam9pbihjbGFzc2lmaWVyX3Njb3JlLGJ5PWMoInNhbXBsZV9pZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiY29tcG9zaXRpb24iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImNlbGxfbGluZV9jb21wb3NpdGlvbiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiY2FuY2VyX3ByZWRpc3Bvc2l0aW9ucyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic2FtcGxlX3R5cGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIktpZHNfRmlyc3RfUGFydGljaXBhbnRfSUQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImNvaG9ydCIpKSAlPiUKICAjIHNlbGVjdCB1c2VmdWwgY29sdW1ucwogIGRwbHlyOjpzZWxlY3Qoc2FtcGxlX2lkLEtpZHNfRmlyc3RfQmlvc3BlY2ltZW5fSURfUk5BLEtpZHNfRmlyc3RfQmlvc3BlY2ltZW5fSURfRE5BLAogICAgICAgICB0cDUzX3Njb3JlLGNhbmNlcl9wcmVkaXNwb3NpdGlvbnMsSEdWU3BfU2hvcnQsY29weV9udW1iZXIsU1YudHlwZSxGdXNpb25OYW1lLGhvdHNwb3QsYWN0aXZhdGluZykgJT4lCiAgYXJyYW5nZShzYW1wbGVfaWQpICU+JQogIHVuaXF1ZSgpICU+JQogIHJlcGxhY2VfbmEobGlzdChob3RzcG90ID0gMCwgYWN0aXZhdGluZyA9IDApKQoKdHA1M19hbHRlcmF0aW9uc19zY29yZSAKCmBgYAoKQ29udmVydCB0aGUgYWJvdmUgdG8gd2lkZSBmb3JtYXQKCmBgYHtyfQoKdHA1M19hbHRlcmF0aW9uc19zY29yZV93aWRlIDwtIHRwNTNfYWx0ZXJhdGlvbnNfc2NvcmUgJT4lCiAgIyBncm91cCAKICBncm91cF9ieShzYW1wbGVfaWQsCiAgICAgICAgICAgS2lkc19GaXJzdF9CaW9zcGVjaW1lbl9JRF9ETkEsCiAgICAgICAgICAgS2lkc19GaXJzdF9CaW9zcGVjaW1lbl9JRF9STkEsCiAgICAgICAgICAgY2FuY2VyX3ByZWRpc3Bvc2l0aW9ucywKICAgICAgICAgICB0cDUzX3Njb3JlKSAlPiUKICAjIAogICMgc2FtcGxlX2lkIGFzIHJvd3MgYWRkIFNOViBjb3VudHMsQ05WIGxvc3MgY291bnRzIAogICMgYW5kIGhvdHNwb3QvYWN0aXZhdGluZyBtdXRhdGlvbiBhbm5vdGF0aW9uICAKICBzdW1tYXJpc2UoCiAgICAjIHN1bW1hcml6ZSBsZW5ndGggb2YgU05WIGFuZCBDTlYgYWx0ZXJhdGlvbnMgCiAgIFNOVl9pbmRlbF9jb3VudHM9IGxlbmd0aCh1bmlxdWUoSEdWU3BfU2hvcnRbIWlzLm5hKEhHVlNwX1Nob3J0KV0pKSwgCiAgICBDTlZfbG9zc19jb3VudHMgPSBsZW5ndGgodW5pcXVlKGNvcHlfbnVtYmVyWyFpcy5uYShjb3B5X251bWJlcildKSksCiAgICBTVl9jb3VudHMgPSBsZW5ndGgodW5pcXVlKFNWLnR5cGVbIWlzLm5hKFNWLnR5cGUpXSkpLAogICAgRnVzaW9uX2NvdW50cyA9IGxlbmd0aCh1bmlxdWUoRnVzaW9uTmFtZVshaXMubmEoRnVzaW9uTmFtZSldKSksCiAgICBIR1ZTcF9TaG9ydCA9IHRvU3RyaW5nKHVuaXF1ZShIR1ZTcF9TaG9ydCkpLAogICAgQ05WX2xvc3NfZXZpZGVuY2UgPSB0b1N0cmluZyh1bmlxdWUoY29weV9udW1iZXIpKSwKICAgIFNWX3R5cGUgPSB0b1N0cmluZyh1bmlxdWUoU1YudHlwZSkpLAogICAgRnVzaW9uX2V2aWRlbmNlID0gdG9TdHJpbmcodW5pcXVlKEZ1c2lvbk5hbWUpKSwKICAgICMgc3VtbWFyaXplIHVuaXF1ZSBob3RzcG90IHZhbHVlcyBwZXIgc2FtcGxlX2lkCiAgICBob3RzcG90ID0gbWF4KHVuaXF1ZShob3RzcG90WyFpcy5uYShob3RzcG90KSBdKSksCiAgICBhY3RpdmF0aW5nID0gbWF4KHVuaXF1ZShhY3RpdmF0aW5nWyFpcy5uYShhY3RpdmF0aW5nKV0pKSkgCgp0cDUzX2FsdGVyYXRpb25zX3Njb3JlX3dpZGUKCmBgYAoKIyMjIEFkZCBhbm5vdGF0aW9uCgpBcyBkaXNjdXNzZWQgYWJvdmUgd2Ugd2FudCB0byBhbm5vdGF0ZSBUUDUzIG11dGFudHMgdGhhdCBhcmUgcHV0YXRpdmUgbG9zcy1vZi1mdW5jdGlvbiBPUiAgZ2Fpbi1vZi1mdW5jdGlvbi4KCmBgYHtyfQoKdHA1M19hbHRlcmF0aW9uc19zY29yZV93aWRlIDwtIHRwNTNfYWx0ZXJhdGlvbnNfc2NvcmVfd2lkZSAlPiUKICBtdXRhdGUoCiAgICAjIGFkZCB0cDUzX2FsdGVyZWQgYW5ub3RhdGlvbiBjb2x1bW4KICAgIHRwNTNfYWx0ZXJlZCA9IAogICAgICBjYXNlX3doZW4oCiAgICAgICAgIyB3aGVuIGFjdGl2YXRpbmcgPT0gMAogICAgICAgIGFjdGl2YXRpbmcgPT0gMCAmCiAgICAgICAgICAjIGNoZWNrIGlmIG11dGF0ZWQgdmFyaWFudCBBQSBwb3NpdGlvbiBvdmVybGFwcyBob3RzcG90IGRhdGFiYXNlIAogICAgICAgICAgKCBob3RzcG90ID09IDEgIHwKICAgICAgICAgICAgICAjIGNoZWNrIGlmIHNhbXBsZV9pZCBoYXMgU05WKyhDTlZ8U1YpIG11dGF0aW9uCiAgICAgICAgICAgICAgIyBzdWdnZXN0aW5nIGJvdGggYWxsZWxlcyBhcmUgbXV0YXRlZAogICAgICAgICAgICAgIChTTlZfaW5kZWxfY291bnRzID49IDEgJiAKICAgICAgICAgICAgICAgICAoQ05WX2xvc3NfY291bnRzID49MSB8IFNWX2NvdW50cyA+PTEpICkgfAogICAgICAgICAgICAgICMgY2hlY2sgaWYgbW9yZSB0aGFuIDEgU05WIGlzIHByZXNlbnQKICAgICAgICAgICAgICAjIHN1Z2dlc3RpbmcgYm90aCBhbGxlbGVzIGFyZSBtdXRhdGVkCiAgICAgICAgICAgICAgU05WX2luZGVsX2NvdW50cyA+IDEgfAogICAgICAgICAgICAgICMgY2hlY2sgaWYgU05WIG11dGFudCBhbmQgaGFzCiAgICAgICAgICAgICAgIyBMaS1GcmF1bWVuaSBzeW5kcm9tZSBzdWdnZXN0aW5nCiAgICAgICAgICAgICAgIyBnZXJtbGluZSBUUDUzIG11dGFudCBhcyBjYW5jZXIgcHJlZGlzcG9zaXRpb24gCiAgICAgICAgICAgICAgKFNOVl9pbmRlbF9jb3VudHMgPj0gMSAmIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdyZXBsKCJMaS1GcmF1bWVuaSBzeW5kcm9tZSIsY2FuY2VyX3ByZWRpc3Bvc2l0aW9ucykpIHwKICAgICAgICAgICAgICAjIGNoZWNrIGlmIENOVnxTViBtdXRhbnQgIGFuZCBoYXMKICAgICAgICAgICAgICAjIExpLUZyYXVtZW5pIHN5bmRyb21lIHN1Z2dlc3RpbmcKICAgICAgICAgICAgICAjIGdlcm1saW5lIFRQNTMgbXV0YW50IGFzIGNhbmNlciBwcmVkaXNwb3NpdGlvbiAKICAgICAgICAgICAgICAoKENOVl9sb3NzX2NvdW50cyA+PSAxIHwgU1ZfY291bnRzID49MSApICYgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JlcGwoIkxpLUZyYXVtZW5pIHN5bmRyb21lIixjYW5jZXJfcHJlZGlzcG9zaXRpb25zKSkgfAogICAgICAgICAgICAgICMgY2hlY2sgaWYgdHA1MyBpbmFjdGl2YXRpbmcgc2NvcmUgZm9yIFJOQV9TZXEgaXMgZ3JlYXRlciB0aGF0IDAuNQogICAgICAgICAgICAgICMgYW5kIGhhcyBMaS1GcmF1bWVuaSBzeW5kcm9tZSBzdWdnZXN0aW5nCiAgICAgICAgICAgICAgIyBnZXJtbGluZSBUUDUzIG11dGFudCBhcyBjYW5jZXIgcHJlZGlzcG9zaXRpb24gCiAgICAgICAgICAgICAgKHRwNTNfc2NvcmUgPiAwLjUgJiAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBncmVwbCgiTGktRnJhdW1lbmkgc3luZHJvbWUiLGNhbmNlcl9wcmVkaXNwb3NpdGlvbnMpKSB8CiAgICAgICAgICAgICAgIyBjaGVjayBpZiB0cDUzIGluYWN0aXZhdGluZyBzY29yZSBmb3IgUk5BX1NlcSBpcyBncmVhdGVyIHRoYXQgMC41CiAgICAgICAgICAgICAgIyBhbmQgaGFzIDEgb3IgbW9yZSBTTlZ8IChDTlZ8U1YpIGxvc3MgaW4gVFA1MwogICAgICAgICAgICAgICh0cDUzX3Njb3JlID4gMC41ICYgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKFNOVl9pbmRlbF9jb3VudHMgPj0gMSB8IChDTlZfbG9zc19jb3VudHMgPj0xIHwgU1ZfY291bnRzID49MSApKSkKICAgICAgICAgICkgfiAibG9zcyIsCiAgICAgICAgIyB3aGVuIGFjdGl2YXRpbmcgPT0gMQogICAgICAgIGFjdGl2YXRpbmcgPT0gMSB+ICJhY3RpdmF0ZWQiLAogICAgICAgICMgaWYgbm8gZXZpZGVuY2Ugc3VwcG9ydHMgVFA1MyBpbmF0aXZhdGlvbiBvciBhY3RpdmF0aW9uIAogICAgICAgIFRSVUUgfiAiT3RoZXIiCiAgICAgICkKICApCgpgYGAKCiMjIEV4cGxvcmUgZGlzdHJpYnV0aW9uIG9mIHRwNTNfYWx0ZXJlZCBzdGF0dXMgdnMgdHA1MyBpbmFjdGl2YXRpb24gc2NvcmVzCgpgYGB7ciwgb3V0LndpZHRoPSI1MCUifQpnZ3Bsb3QodHA1M19hbHRlcmF0aW9uc19zY29yZV93aWRlLCBhZXMoeCA9IGZhY3Rvcih0cDUzX2FsdGVyZWQpLCB5ID0gdHA1M19zY29yZSkpICsKICBnZW9tX3Zpb2xpbigpKwogIGdlb21faml0dGVyKGFscGhhID0gMC41LCB3aWR0aCA9IDAuMikgKwogIHN0YXRfY29tcGFyZV9tZWFucygpICsKICB0aGVtZV9idygpICsKICBnZ3RpdGxlKCJEaXN0cmlidXRpb24gb2Ygc2NvcmVzIGFjcm9zcyB0cDUzIGFsdGVyZWQgc3RhdHVzIikgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNjAsIGhqdXN0ID0gMSkpKwogIHhsYWIoInRwNTMgYWx0ZXJlZCBzdGF0dXMiKSAKCmBgYApUUDUzIG11dGFudHMgd2l0aCBnYWluLW9mLWZ1bmN0aW9uIChhY3RpdmF0aW5nKSBtdXRhbnRzIHByb21vdGUgdHVtb3JpZ2VuZXNpcyBhY2NvcmRpbmcgdG8gbGl0ZXJhdHVyZS4gW1JlZmVyZW5jZV0oaHR0cHM6Ly9wdWJtZWQubmNiaS5ubG0ubmloLmdvdi8xNzQxNzYyNy8pIGFuZCBbcmVmZXJlbmNlXShodHRwczovL3B1Ym1lZC5uY2JpLm5sbS5uaWguZ292LzI0Njc3NTc5LykgaGF2ZSBzaW1pbGFyIGRpc3RyaWJ1dGlvbiBvZiBjbGFzc2lmaWVyIHNjb3JlcyB0byB0aGF0IG9mIHBvdGVudGlhbCBsb3NzLW9mLWZ1bmN0aW9uIChtdWx0aS1hbGxlbGljKSBUcDUzIG11dGF0aW9ucy4gCgoiT3RoZXIiIGFubm90YXRlZCBzYW1wbGVzIGVpdGhlciBkb24ndCBoYXZlIGFueSBoaWdoLWNvbmZpZGVuY2UgbG9zcy9nYWluIFRQNTMgU05WcyBub3IgQ05WIGxvc3NlcyBPUiBETkEgc2FtcGxlIGlzIG5vdCBhdmFpbGFibGUuCgoKIyMjIEV4cHJlc3Npb24gcHJvZmlsZSBmb3IgYWN0aXZhdGluZyB2cyBsb3NzIFRQNTMgc3RhdHVzCmBgYHtyfQoKZXhwcl9jb21iaW5lZCA8LSByZWFkUkRTKGZpbGUucGF0aChkYXRhX2RpciwiZ2VuZS1jb3VudHMtcnNlbS1leHBlY3RlZF9jb3VudC1jb2xsYXBzZWQucmRzIikpCgpgYGAKCiMjIyMgRXhwcmVzc2lvbiBwcm9maWxlIGZvciBhY3RpdmF0aW5nIHZzIGxvc3MgVFA1MyBzdGF0dXMKCmBgYHtyLCBvdXQud2lkdGg9IjUwJSJ9CiMgaXRlcmF0ZSB0aHJvdWdoIGFsbCBwb3NzaWJsZSBSTkEgbGlicmFyeSB0eXBlIAojIGZpcnN0IGZpbHRlciB0byAKY2xpbmljYWxfZmlsdGVyZWQgPC0gY2xpbmljYWwgJT4lIAogIGZpbHRlcihLaWRzX0ZpcnN0X0Jpb3NwZWNpbWVuX0lEICVpbiUgbmFtZXMoZXhwcl9jb21iaW5lZCkpCgojIGdlbmVyYXRlIGxpc3Qgb2YgYWxsIFJOQSBsaWJyYXJ5IHR5cGUKcm5hX2xpYnJhcnlfbGlzdCA8LSBjbGluaWNhbF9maWx0ZXJlZCAlPiUgCiAgZmlsdGVyKEtpZHNfRmlyc3RfQmlvc3BlY2ltZW5fSUQgJWluJSBuYW1lcyhleHByX2NvbWJpbmVkKSkgJT4lCiAgcHVsbChSTkFfbGlicmFyeSkgJT4lIAogIHVuaXF1ZSgpCgpmb3IoaSBpbiAxOmxlbmd0aChybmFfbGlicmFyeV9saXN0KSkgewogIAogIGxpYnJhcnlfZWFjaCA8LSBybmFfbGlicmFyeV9saXN0W1tpXV0KICAjIHB1bGwgQlMgSURzIGZvciB0aGF0IFJOQSBsaWJyYXJ5IHR5cGUKICBsaWJyYXJ5X3NhbXBsZXMgPC0gY2xpbmljYWxfZmlsdGVyZWQgJT4lIAogICAgZmlsdGVyKFJOQV9saWJyYXJ5ID09IGxpYnJhcnlfZWFjaCkgJT4lCiAgICBwdWxsKEtpZHNfRmlyc3RfQmlvc3BlY2ltZW5fSUQpICU+JSAKICAgIHVuaXF1ZSgpCiAgIyBzdWJzZXQgZXhwcmVzc2lvbiBtYXRyaXggdXNpbmcgdGhhdAogIGV4cHJfZWFjaCA8LSBleHByX2NvbWJpbmVkICU+JSAKICAgIGRwbHlyOjpzZWxlY3QobGlicmFyeV9zYW1wbGVzKQoKICAjIHN1YnNldCB0byBUUDUzCiAgc3Vic2V0X2V4cHJfZWFjaCA8LSB0KGV4cHJfZWFjaClbLCJUUDUzIl0gJT4lIAogICAgYXMuZGF0YS5mcmFtZSgpIAogIGNvbG5hbWVzKHN1YnNldF9leHByX2VhY2gpIDwtICJUUDUzIgogIAogICMgY29tYmluZSB0aGUgVFA1MyBhbHRlcmF0aW9uIGluZm9ybWF0aW9uCiAgc3Vic2V0X2V4cHJfY29tYmluZWQgPC0gc3Vic2V0X2V4cHJfZWFjaCAlPiUKICAgIHRpYmJsZTo6cm93bmFtZXNfdG9fY29sdW1uKCkgJT4lIAogICAgbGVmdF9qb2luKHRwNTNfYWx0ZXJhdGlvbnNfc2NvcmVfd2lkZSxieT1jKCJyb3duYW1lIj0iS2lkc19GaXJzdF9CaW9zcGVjaW1lbl9JRF9STkEiKSkgJT4lCiAgICBmaWx0ZXIodHA1M19hbHRlcmVkICVpbiUgYygiYWN0aXZhdGVkIiwibG9zcyIpKQogIAogICMgcGxvdCBkaXN0cmlidXRpb24gb2YgVFA1MyBnZW5lIGV4cHJlc3Npb24gCiAgcGxvdDwtIGdncGxvdChzdWJzZXRfZXhwcl9jb21iaW5lZCwgYWVzKHggPSBmYWN0b3IodHA1M19hbHRlcmVkKSwgeSA9IFRQNTMpKSArCiAgICBnZW9tX3Zpb2xpbigpKwogICAgZ2VvbV9qaXR0ZXIoYWxwaGEgPSAwLjUsIHdpZHRoID0gMC4yKSArCiAgICBzdGF0X2NvbXBhcmVfbWVhbnMoKSArCiAgICB0aGVtZV9idygpICsKICAgIGdndGl0bGUoIkRpc3RyaWJ1dGlvbiBvZiBUUDUzIGV4cHJlc3Npb24gYWNyb3NzIHRwNTMgYWx0ZXJlZCBzdGF0dXMiKSArCiAgICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDYwLCBoanVzdCA9IDEpKSsKICAgIHhsYWIocGFzdGUwKCJ0cDUzIGFsdGVyZWQgc3RhdHVzIHd0aCBSTkEgbGlicmFyeSAiLCBsaWJyYXJ5X2VhY2gpKQoKICBwcmludChwbG90KQp9CgoKYGBgCgoKIyMjIENoZWNrIGlmIG90aGVyIGNhbmNlciBwcmVkaXNwb3NpdGlvbiBoYXZlIGhpZ2ggVFA1MyBpbmFjdGl2YXRpb24gc2NvcmVzICAKCmBgYHtyICxvdXQud2lkdGg9IjUwJSJ9CgpnZ3Bsb3QodHA1M19hbHRlcmF0aW9uc19zY29yZV93aWRlLCBhZXMoeCA9IGZhY3Rvcih0cDUzX2FsdGVyZWQpLCB5ID0gdHA1M19zY29yZSkpICsKICBnZW9tX3Zpb2xpbigpKwogIGdlb21faml0dGVyKGFscGhhID0gMC41LCB3aWR0aCA9IDAuMikgKwogIHRoZW1lX2J3KCkgKwogIGdndGl0bGUoIkRpc3RyaWJ1dGlvbiBvZiBzY29yZXMgYWNyb3NzIHRwNTMgYWx0ZXJlZCBzdGF0dXMiKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA2MCwgaGp1c3QgPSAxKSkrCiAgeGxhYigidHA1MyBhbHRlcmVkIHN0YXR1cyIpICsKICBmYWNldF93cmFwKC5+Y2FuY2VyX3ByZWRpc3Bvc2l0aW9ucykKCmBgYApTb21lIE5GLTEgYW5kL29yIE90aGVyIGluaGVyaXRlZCBjb25kaXRpb25zIE5PUyBoYXZlIGhpZ2ggc2NvcmluZyBUUDUzIG11dGFudHMgYXMgd2VsbC4gCgpJbnRlcmVzdGluZyAyIGJzIGlkcyBhbm5vdGF0ZWQgd2l0aCBMaS1GcmF1bWVuaSBzeW5mcm9tZSBwcmUtZGlzcG9zaXRpb24gaGF2ZSB2ZXJ5IGxvdyB0cDUzIGNsYXNzaWZpZXIgc2NvcmVzLgoKYGBge3J9Cgp0cDUzX2FsdGVyYXRpb25zX3Njb3JlX3dpZGUgJT4lCiAgZmlsdGVyKGNhbmNlcl9wcmVkaXNwb3NpdGlvbnMgPT0gIkxpLUZyYXVtZW5pIHN5bmRyb21lIiwKICAgICAgICAgdHA1M19zY29yZSA8IDAuNSkgCiAgCmBgYAoKCiMjIFNhdmUgZmlsZSAKCkFkZGluZyBETkEgYW5kIFJOQSBiaW9zcGVjaW1lbiBpZHMgdG8gYHRwNTNfYWx0ZXJhdGlvbnNfc2NvcmVfd2lkZWAgYW5kIHNhdmluZwoKYGBge3J9Cgp0cDUzX2FsdGVyYXRpb25zX3Njb3JlX3dpZGUgJT4lIAogIHdyaXRlX3RzdihmaWxlLnBhdGgocmVzdWx0c19kaXIsInRwNTNfYWx0ZXJlZF9zdGF0dXMudHN2IikpCgpgYGAK